package org.hiucung.hlsdownloader.viewmodel

import android.app.Application
import android.os.Environment
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.hiucung.hlsdownloader.utils.M3U8KeyLoader
import org.hiucung.hlsdownloader.bean.M3U8TaskItem
import org.hiucung.hlsdownloader.bean.M3U8TaskState
import org.hiucung.hlsdownloader.database.M3U8Database
import org.hiucung.hlsdownloader.database.M3U8TaskStateConverters
import org.hiucung.hlsdownloader.database.M3U8TaskItemEntity
import org.hiucung.hlsdownloader.utils.FileUtils
import org.hiucung.hlsdownloader.utils.M3U8Parser
import org.hiucung.hlsdownloader.utils.OkioAesDecrypt
import org.hiucung.hlsdownloader.utils.TsMerger
import java.io.File
import java.io.FileOutputStream
import java.net.URL

/**
 * @Classname M3U8ViewModel
 * @Description M3U8 解析下载与合成 ViewModel
 * @Date 2025/8/21 14:46
 * @Created by hiucung
 */
class M3U8ViewModel(application: Application) : AndroidViewModel(application) {
    private val database = M3U8Database.getInstance(application)
    private val downloadItemDao = database.m3u8TaskItemDao()

    // 同时最多3个下载任务
    private val downloadSemaphore = Semaphore(3)

    // 使用 Flow 从数据库获取数据
    // 将数据库实体转换为 UI实体
    val downloadItems: Flow<List<M3U8TaskItem>> = downloadItemDao.getAll().map { entities ->
        entities.map { entity ->
            M3U8TaskItem(
                id = entity.id,
                url = entity.url,
                name = entity.name,
                status = converters.toM3UTaskState(entity.status),
                currentTaskId = null, // 根据需求设置
                outputPath = entity.outputPath,
                downloadedSegments = entity.downloadedSegments,
                totalSegments = entity.totalSegments
            )
        }
    }
    private val client = OkHttpClient()
    private val tsMerger = TsMerger()

    private val converters = M3U8TaskStateConverters()

    fun addDownloadItem(url: String, taskName: String = "M3U_${System.currentTimeMillis()}") {
        viewModelScope.launch(Dispatchers.IO) {
            val newItem = M3U8TaskItemEntity(
                id = System.currentTimeMillis().toString(),
                url = url,
                name = taskName,
                status = converters.fromM3UTaskState(M3U8TaskState.WaitingDownload),
                createdAt = System.currentTimeMillis(),
                updatedAt = System.currentTimeMillis(),
                totalSegments = 0,
                downloadedSegments = 0
            )
            downloadItemDao.insert(newItem)

            // 使用Semaphore控制并发
            try {
                // 获取许可，如果没有可用许可则阻塞
                downloadSemaphore.acquire()
                // 自动开始下载
                downloadAllTS(newItem.id)
            } finally {
                downloadSemaphore.release() // 释放许可
            }
        }
    }

    /**
     * 从 M3U8 播放列表下载所有 TS 文件
     * @param m3u8Url M3U8 文件URL
     * @param outputDir 输出目录
     * @param context 上下文（用于获取存储路径）
     */
    /**
     * 从 M3U8 播放列表下载所有 TS 文件
     */
    private suspend fun downloadAllTS(itemId: String) {

        try {
            // 获取任务项
            val itemEntity = downloadItemDao.getById(itemId) ?: return

            // 是否为加密 TS
            var isEncrypt = false

            // 更新状态为等待下载
            updateItemState(itemId, M3U8TaskState.WaitingDownload)

            val downloadsDir = File(
                "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path}/$itemId"
            )

            // 1. 解析 M3U8 文件获取 TS 列表
            val parser = M3U8Parser()
            val playlist = parser.parseUrl(itemEntity.url)
            val tsItems = playlist.items

            if (tsItems.isEmpty()) {
                updateItemState(itemId, M3U8TaskState.Error("未找到TS文件"))
                return
            }

            // 更新总片段数
            updateItemTotalSegments(itemId, tsItems.size)

            // 2. 依次下载每个 TS 文件
            val downloadedTsFiles = mutableListOf<String>()
            val downloadedEncryptTsFiles = mutableListOf<String>()

            tsItems.forEachIndexed { index, tsItem ->
                // 更新下载状态和进度
                updateItemState(
                    itemId,
                    M3U8TaskState.Downloading(
                        current = index + 1,
                        total = tsItems.size,
                        currentUrl = tsItem.url
                    )
                )
                val tsFile = downloadTS(
                    tsUrl = resolveUrl(itemEntity.url, tsItem.url),
                    outputDir = downloadsDir,
                    filename = "segment_${index + 1}"
                )

//                delay(2000)

                downloadedTsFiles.add(tsFile.absolutePath)

                // 如果是加密 ts还需要解密
                if (tsItem.encryptionKey != null) {
                    isEncrypt = true

                    val decryptFile = File(downloadsDir, "segment_${index + 1}_decrypt")
                    OkioAesDecrypt().decryptFile(
                        tsFile,
                        decryptFile,
                        M3U8KeyLoader.loadKey(tsItem.encryptionKey) ?: ByteArray(16),
                        tsItem.encryptionKey.getIvBytes() ?: ByteArray(16)
                    )

                    downloadedEncryptTsFiles.add(decryptFile.absolutePath)
                }
            }

            updateItemState(
                itemId,
                M3U8TaskState.DownloadCompleted(
                    if (isEncrypt) downloadedEncryptTsFiles else downloadedTsFiles
                )
            )

            // 合并视频，完成之后删除所有片段
            mergeWithOkio(
                itemId,
                if (isEncrypt) downloadedEncryptTsFiles else downloadedTsFiles,
                downloadsDir.absolutePath + "/output_${itemId}.ts"
            ) {
                if (isEncrypt) {
                    FileUtils.deleteFiles(downloadedEncryptTsFiles)
                }
                FileUtils.deleteFiles(downloadedTsFiles)
            }

        } catch (e: Exception) {
            updateItemState(itemId, M3U8TaskState.Error(e.message ?: "下载失败"))
        }

    }


    /**
     * 下载单个 TS 文件
     */
    private suspend fun downloadTS(tsUrl: String, outputDir: File, filename: String): File {
        return withContext(Dispatchers.IO) {
            val request = Request.Builder().url(tsUrl).build()
            val response = client.newCall(request).execute()

            if (!response.isSuccessful) {
                throw Exception("下载失败: HTTP ${response.code}")
            }

            response.body?.use { body ->
                val tsFile = File(outputDir, filename).apply {
                    parentFile?.mkdirs()
                }

                FileOutputStream(tsFile).use { output ->
                    body.byteStream().copyTo(output)
                }

                return@withContext tsFile
            } ?: throw Exception("空响应体")
        }
    }

    /**
     * 解析相对路径为完整URL
     */
    private fun resolveUrl(baseUrl: String, relativeUrl: String): String {
        return if (relativeUrl.startsWith("http")) {
            relativeUrl
        } else {
            val base = URL(baseUrl)
            URL(base, relativeUrl).toString()
        }
    }

    // 使用Okio合并（推荐）
    private suspend fun mergeWithOkio(
        itemId: String,
        tsFilePaths: List<String>,
        outputPath: String,
        beforeHandleMerge: (suspend () -> Unit)? = null
    ) {
        updateItemState(itemId, M3U8TaskState.WaitingMerge)

        val result = tsMerger.mergeTsFilesWithOkio(tsFilePaths, outputPath) { current, total ->
            updateItemState(itemId, M3U8TaskState.Merging(current, total))
        }

        beforeHandleMerge?.invoke()

        handleMergeResult(itemId, result)
    }

    // 使用标准IO合并
    private suspend fun mergeWithStandardIO(
        itemId: String,
        tsFilePaths: List<String>,
        outputPath: String
    ) {
        updateItemState(itemId, M3U8TaskState.WaitingMerge)

        val result = tsMerger.mergeTsFiles(tsFilePaths, outputPath) { current, total ->
            updateItemState(itemId, M3U8TaskState.Merging(current, total))
        }

        FileUtils.deleteFiles(tsFilePaths)

        handleMergeResult(itemId, result)
    }

    private suspend fun handleMergeResult(itemId: String, result: Result<File>) {
        val newState = when {
            result.isSuccess -> M3U8TaskState.MergeCompleted(result.getOrNull()?.absolutePath ?: "")
            else -> M3U8TaskState.Error(result.exceptionOrNull()?.message ?: "未知错误")
        }
        updateItemState(itemId, newState)
    }

    // 更新单个项目的状态
    private suspend fun updateItemState(itemId: String, newState: M3U8TaskState) {
        val item = downloadItemDao.getById(itemId) ?: return
        val outFilePath = if (newState is M3U8TaskState.MergeCompleted) {
            newState.outFilePath
        } else {
            null
        }
        val current = when (newState) {
            is M3U8TaskState.Downloading -> {
                newState.current
            }

            is M3U8TaskState.Merging -> {
                newState.current
            }

            else -> {
                0
            }
        }
        val total = when (newState) {
            is M3U8TaskState.Downloading -> {
                newState.total
            }

            is M3U8TaskState.Merging -> {
                newState.total
            }

            else -> {
                0
            }
        }
        val updatedItem = item.copy(
            status = converters.fromM3UTaskState(newState),
            outputPath = outFilePath,
            updatedAt = System.currentTimeMillis(),
            downloadedSegments = current,
            totalSegments = total
        )
        downloadItemDao.update(updatedItem)
    }

    // 更新总片段数
    private suspend fun updateItemTotalSegments(itemId: String, totalSegments: Int) {
        val item = downloadItemDao.getById(itemId) ?: return
        val updatedItem = item.copy(
            totalSegments = totalSegments,
            updatedAt = System.currentTimeMillis()
        )
        downloadItemDao.update(updatedItem)
    }

    // 删除任务
    fun deleteItem(itemId: String) {
        viewModelScope.launch(Dispatchers.IO) {
            downloadItemDao.deleteById(itemId)

            // 同时删除对应的下载文件
            val downloadsDir = File(
                "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path}/$itemId"
            )
            if (downloadsDir.exists()) {
                downloadsDir.deleteRecursively()
            }
        }
    }

    // 重新开始任务
    fun restartItem(itemId: String) {
        viewModelScope.launch(Dispatchers.IO) {
            updateItemState(itemId, M3U8TaskState.WaitingDownload)
            downloadAllTS(itemId)
        }
    }
}